<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Detailed custom validation</title>
    <style>
      body {
        font: 1em sans-serif;
        width: 200px;
        padding: 0;
        margin : 0 auto;
      }

      p * {
        display: block;
      }

      input[type=email]{
        -webkit-appearance: none;
        appearance: none;

        width: 100%;
        border: 1px solid #333;
        margin: 0;

        font-family: inherit;
        font-size: 90%;

        box-sizing: border-box;
      }

      /* This is our style for the invalid fields */
      input:invalid{
        border-color: #900;
        background-color: #FDD;
      }

      input:focus:invalid {
        outline: none;
      }

      /* This is the style of our error messages */
      .error {
        width  : 100%;
        padding: 0;

        font-size: 80%;
        color: white;
        background-color: #900;
        border-radius: 0 0 5px 5px;

        box-sizing: border-box;
      }

      .error.active {
        padding: 0.3em;
      }
    </style>
  </head>

<body>
  <form novalidate>
    <p>
      <label for="mail">
        <span>Please enter an email address:</span>
        <input type="email" id="mail" name="mail" required minlength="8">
        <span class="error" aria-live="polite"></span>
      </label>
    </p>
    <button>Submit</button>
  </form>

  <script>
    // There are many ways to pick a DOM node; here we get the form itself and the email
    // input box, as well as the span element into which we will place the error message.
    const form  = document.getElementsByTagName('form')[0];

    const email = document.getElementById('mail');
    const emailError = document.querySelector('#mail + span.error');

    email.addEventListener('input', function (event) {
      // Each time the user types something, we check if the
      // form fields are valid.

      if (email.validity.valid) {
        // In case there is an error message visible, if the field
        // is valid, we remove the error message.
        emailError.innerHTML = ''; // Reset the content of the message
        emailError.className = 'error'; // Reset the visual state of the message
      } else {
        // If there is still an error, show the correct error
        showError();
      }
    });

    form.addEventListener('submit', function (event) {
      // if the form contains valid data, we let it submit

      if(!email.validity.valid) {
        // If it isn't, we display an appropriate error message
        showError();
        // Then we prevent the form from being sent by canceling the event
        event.preventDefault();
      }
    });

    function showError() {
      if(email.validity.valueMissing) {
        // If the field is empty
        // display the following error message.
        emailError.textContent = 'You need to enter an e-mail address.';
      } else if(email.validity.typeMismatch) {
        // If the field doesn't contain an email address
        // display the following error message.
        emailError.textContent = 'Entered value needs to be an e-mail address.';
      } else if(email.validity.tooShort) {
        // If the data is too short
        // display the following error message.
        emailError.textContent = `Email should be at least ${ email.minLength } characters; you entered ${ email.value.length }.`;
      }

      // Set the styling appropriately
      emailError.className = 'error active';
    }
  </script>
</body>

</html>
